#include "sprite.h"

Sprite::Sprite(char *Path, int Width, int Height, int Count, int SpritePerRow) 
{
	_Width = Width;
	_Height = Height;
	_Count = Count;
	_SpritePerRow = SpritePerRow;
	_Index = 0;
    
    m_uiColumns = 1;
    m_uiRows = 1;
    
    CreateMesh();
    
    m_puiIndexVbo = new GLuint[eIndexVboNum];
    // Front Index Vbo
    glGenBuffers(1, m_puiIndexVbo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[eIndexVbo]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_uiStripLength * sizeof(GLushort), m_auiIndices, GL_STATIC_DRAW);
    
    m_fCameraNear = 1.0f;
    m_fCameraFar = 100.0f;
    m_fFOV = 90.0f * PVRT_PI / 180.0f;
    
    CPVRTString ErrorStr;
    if (!LoadShader(&ErrorStr))
        PVRTErrorOutputDebug(ErrorStr.c_str());

    PVRTVECTOR3	vFrom = {0.0f, 0.0f, 4.0f },
                vTo = { 0, 0, 0 },
                vUp = { 0, 1, 0 };
    
    bool bRotate = false;
    PVRTMatrixPerspectiveFovRH(m_mProjection, m_fFOV, (float)Width / (float)Height, m_fCameraNear, m_fCameraFar, bRotate);
    
    PVRTMatrixLookAtRH(m_mView, vFrom, vTo, vUp);
}

/*!****************************************************************************
 @Function		LoadShaders
 @Output		pErrorStr		A string describing the error on failure
 @Return		bool			true if no error occured
 @Description	Loads and compiles the shaders and links the shader programs
 required for this training course
 ******************************************************************************/
bool Sprite::LoadShader(CPVRTString* pErrorStr)
{
    m_puiVertShader = new GLuint[eShaderNum];
    m_puiFragShader = new GLuint[eShaderNum];
    
    PVRTShaderLoadFromFile("", "shCurlVShader.vsh", GL_VERTEX_SHADER, 0, &m_puiVertShader[eShader], pErrorStr);
    PVRTShaderLoadFromFile("", "shCurlFShader.fsh", GL_FRAGMENT_SHADER, 0, &m_puiFragShader[eShader], pErrorStr);
    
    /*
     Set up and link the shader program
     */
    const char* aszAttribs[] = { "rm_Vertex", "rm_TexCoord" };
    
    PVRTCreateProgram(&m_ShaderProgram.uiId, m_puiVertShader[eShader], m_puiFragShader[eShader], aszAttribs, 2, pErrorStr);
    
    // Set the sampler2D variable to the first texture unit
    glUniform1i(glGetUniformLocation(m_ShaderProgram.uiId, "sTexture"), 0);
    
    // Store the location of uniforms for later use
    m_ShaderProgram.uiMVPMatrixLoc = glGetUniformLocation(m_ShaderProgram.uiId, "rm_MVMatrix");
    
    return true;
}

void Sprite::CreateMesh()
{
    GLushort vCountX = (GLushort)(m_uiColumns + 1);     // Number of vertices along the x axis
    GLushort vCountY = (GLushort)(m_uiRows + 1);	    // Number of vertices along the y axis
    
    m_uiNumVertices = (GLushort)(vCountX * vCountY);
    DELETE(m_av3Vertices);
    m_av3Vertices = new PVRTVec3[m_uiNumVertices];
    
    DELETE(m_av2TexCoord);
    m_av2TexCoord = new PVRTVec2[m_uiNumVertices];
    
    int vi = 0;	// vertex index
    int iiX, iiY;
    float px, py;
    
    for (iiY = 0; iiY < vCountY; iiY++)
    {
        for (iiX = 0; iiX < vCountX; iiX++)
        {
            px = (float)iiX * 1.0f / m_uiColumns;
            py = (float)iiY * 1.0f / m_uiRows;
            m_av3Vertices[vi].x = px;
            m_av3Vertices[vi].y = py;
            m_av3Vertices[vi].z = 0.0f;
            m_av2TexCoord[vi].x = (float)iiX / m_uiColumns;
            m_av2TexCoord[vi].y = (float)iiY / m_uiRows;
            vi++;
        }
    }
    
    CreateTriangleStrip();
}

void Sprite::CreateTriangleStrip()
{
    // Standard algorithm for tesselating a grid into an optimized triangle strip without resorting to a complex Hamiltonian algorithm.
    GLushort vCountX = (GLushort)(m_uiColumns + 1); // Number of vertices along the x axis
    GLushort vCountY = (GLushort)(m_uiRows + 1);    // Number of vertices along the y axis
    
    m_uiStripLength = (GLushort)((vCountX * 2) * (vCountY - 1) + (vCountY - 2));
    DELETE(m_auiIndices);
    m_auiIndices = new GLushort[m_uiStripLength];
    
    // Construct a triangle strip by scanning back and forth up our mesh, inserting degenerate triangles as necessary
    // to link adjacent rows.
    int iiX, iiY;
    int rowOffset, index = 0;
    bool lastRow, oddRow;
    for (iiY = 0; iiY < m_uiRows; iiY++)
    {
        // For the front, go right to left for odd rows, left to right for even rows. Weaving back and forth rather
        // than always restarting each row on the same side allows us the graphics hardware to reuse cached vertex
        // calculations, per Apple's best practices.
        // Build the back at the same time by scanning in reverse.
        rowOffset = iiY * vCountX;
        lastRow = (iiY == m_uiRows);
        oddRow = (iiY & 1) != 0;
        for (iiX = 0; iiX <= m_uiColumns; iiX++)
        {
            if (oddRow)
            {
                m_auiIndices[index++] = (GLushort)(rowOffset + m_uiColumns - iiX + vCountX);
                m_auiIndices[index++] = (GLushort)(rowOffset + m_uiColumns - iiX);
            }
            else
            {
                m_auiIndices[index++] = (GLushort)(rowOffset + iiX + vCountX);
                m_auiIndices[index++] = (GLushort)(rowOffset + iiX);
            }
        }
        // Unless we're on the last row, insert a degenerate vertex to enable us to connect to the next row.
        if (!lastRow)
        {
            if (oddRow)
            {
                m_auiIndices[index] = (GLushort)(rowOffset + vCountX);
            }
            else
            {
                if (index < m_uiStripLength)
                {
                    m_auiIndices[index] = (GLushort)(rowOffset + vCountX + m_uiColumns);
                }
            }
            index++;
        }
    }
}

void Sprite::Render(int X, int Y)
{
    //	RECT srect;
    //
    //	srect.left = (_Index % _SpritePerRow)*(_Width);
    //	srect.top = (_Index / _SpritePerRow)*(_Height);
    //	srect.right = srect.left + _Width  ;
    //	srect.bottom = srect.top + _Height ;
    //
    //	D3DXVECTOR3 position((float)X,(float)Y,0);
    //
    //	_SpriteHandler->Draw(
    //		_Image,
    //		&srect,
    //		NULL,
    //		&position, 
    //		D3DCOLOR_XRGB(255,255,255)
    //	);
    GLushort vCountX = (GLushort)(m_uiColumns + 1);     // Number of vertices along the x axis
    GLushort vCountY = (GLushort)(m_uiRows + 1);	    // Number of vertices along the y axis
    
    int vi = 0;	// vertex index
    int iiX, iiY;
    
    for (iiY = 0; iiY < vCountY; iiY++)
    {
        for (iiX = 0; iiX < vCountX; iiX++)
        {
            m_av2TexCoord[vi].x = ((_Index + iiX) % _SpritePerRow) * ((float)1 / _SpritePerRow);
            m_av2TexCoord[vi].y = ((_Index + iiY) / _SpritePerRow) * ((float)1);
            vi++;
        }
    }
    
    // Use shader program
    glUseProgram(m_ShaderProgram.uiId);
    
    PVRTMat4 mScale, mTranslate;
    PVRTMatrixScaling(mScale, (float)m_iWidth / (float)m_iHeight * 4.0f, 8.0f, 1.0f);
    
    PVRTMatrixTranslation(mTranslate, -1.0f, -0.5f, 0.0f);
    
    PVRTMat4 mModelView, mMVP;
    mModelView = m_mView * mScale * mTranslate;
    
    PVRTMatrixScaling(mScale, 2.0f, 1.0f, 1.0f);
    mModelView = mModelView * mScale;
    
    mMVP = m_mProjection * mModelView;
    
    glUniformMatrix4fv(m_ShaderProgram.uiMVPMatrixLoc, 1, false, mMVP.f);
    
    glEnableVertexAttribArray(VERTEX_ARRAY);
    glEnableVertexAttribArray(TEXCOORD_ARRAY);
    
    // Set the vertex attribute offsest
    glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, false, sizeof(float) * 3, m_av3Vertices);
    glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, false, sizeof(float) * 2, m_av2TexCoord);
    
    if (m_puiTextureIDs != NULL)
    {
        glActiveTexture(GL_TEXTURE0);
        
        //if (m_puiTextureIDs > m_iIndex)
        //{
            // draw front page
            glBindTexture(GL_TEXTURE_2D, m_puiTextureIDs[_Index]);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_puiIndexVbo[eIndexVbo]);
            glDrawElements(GL_TRIANGLE_STRIP, m_uiStripLength, GL_UNSIGNED_SHORT, 0);
            
            // unbind the vertex buffers as we don't need them bound anymore
            glBindTexture(GL_TEXTURE_2D, 0);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        //}
    }
    
    glDisableVertexAttribArray(VERTEX_ARRAY);
    glDisableVertexAttribArray(TEXCOORD_ARRAY);
}

void Sprite::GenTexture(char* filename, GLuint* texName)
{
    if (PVRTTextureLoadFromPVR(filename, texName) != PVR_SUCCESS)
	{
		PVRTErrorOutputDebug("ERROR: Cannot load the texture\n");
	}
    
    //  Specify linear filtering.
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

void Sprite::Next() 
{
	_Index = (_Index+1)%_Count;
}

void Sprite::Next1() 
{
    if (_Index == 0)
        _Index = 1;
	else
        _Index = 0;	
}

void Sprite::Next2() 
{
    if (_Index == 2)
		_Index = 1;
	else
        _Index = 2;	
}

void Sprite::Next3() 
{
    if (_Index == 3)
		_Index = 1;
	else
        _Index = 3;	
}

void Sprite::Next4() 
{	if (_Index == 4)
		_Index = 1;
	else
        _Index = 4;	
}

void Sprite::Next5() 
{
    if (_Index == 5)
		_Index = 1;
	else
        _Index = 5;	
}

void Sprite::Next6() 
{
    if (_Index == 6)
		_Index = 1;
	else
        _Index = 6;	
}

void Sprite::Next7() 
{
    if (_Index == 7)
		_Index = 1;
	else
        _Index = 7;	
}